package com.sifang.POI.dianping;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.Session;
import org.htmlcleaner.TagNode;
import org.htmlcleaner.XPatherException;

public class JobCommand extends MultiThreadsCommand {
	
	private int shopsProcessedCount;
	
	public JobCommand() {
		this(DEFAULT_MAX_THREADS);
	}

	public JobCommand(int maxThreads) {
		super(maxThreads);
	}
	
	public List<Shop> fetchShops(String urlString) throws IOException, XPatherException {
		Integer cityId = parseCityId(urlString);
		List<Shop> shops = new ArrayList<Shop>();
		TagNode node = WebUtil.cleanUrl(urlString);
		Object[] results = node.evaluateXPath("//div[@id='searchList']/dl/dd");
		for (Object result : results) {
			Shop shop = parseShop((TagNode) result);
			shop.setCityId(cityId);
			shops.add(shop);
			if (shop.getDistrictId() == null) {
				String status = String.format("\ncan't get district id of shop %d from %s", shop.getShopId(), urlString);
				System.out.println(status);
			}
		}
		return shops;
	}
	
	private Integer parseCityId(String urlString) {
		Pattern p = Pattern.compile("category/(\\d+)/");
		Matcher m = p.matcher(urlString);
		Integer cityId = null;
		if (m.find()) {
			String value = m.group(1);
			try {
				cityId = Integer.parseInt(value);
			} catch (NumberFormatException e) {
			}
		}
		return cityId;
	}
	
	private void markJobProcessed(String url, Session s, int totalShop) {
		String query = String.format("update Job job set job.status = %d, job.totalShop = %d where job.url = '%s'),",
				JobStatus.STATUS_PROCESSED, totalShop, url);
		s.createQuery(query).executeUpdate();
	}
	
	private void saveShops(List<Shop> shops, Session s) {
		for (Shop shop : shops) {
			Shop sp = (Shop) s.get(Shop.class, shop.getShopId());
			if (sp == null){
				s.saveOrUpdate(shop);
				System.out.print(String.format("%-20s", "\radd " + shop.getShopId()));
			}
		}
	}
	
	private Shop parseShop(TagNode shopNode) throws XPatherException {
		Shop shop = new Shop();
		
		Object[] results = null;
		String value = null;
		int flag = 0;
		
		// grades
		results = shopNode.evaluateXPath("//li[@class='grade']/span");
		
		if (results.length != 0) {
			flag += 1;
			StringBuilder sb = new StringBuilder();
			for (Object result : results) {
				value = ((TagNode) result).getText().toString();
				sb.append(value).append(',');
			}
			if (sb.length() > 0) {
				sb.deleteCharAt(sb.length() - 1);
			}
			shop.setGrades(sb.toString());
		}
		
		// mark
		results = shopNode.evaluateXPath(String.format("/ul[1]/li[%d]/span", flag + 1));
		
		if (results.length != 0) {
			value = ((TagNode) results[0]).getAttributeByName("class");
			String markPrefix = "irr-star";
			String markString = value.substring(value.indexOf(markPrefix) + markPrefix.length());
			int mark = Integer.parseInt(markString);
			shop.setMark(mark);
		}
		
		// shop_id, total_comment
		results = shopNode.evaluateXPath(String.format("/ul[1]/li[%d]/a", flag + 2));
		
		if (results.length != 0) {
			TagNode tagNode = (TagNode) results[0];
			value = tagNode.getAttributeByName("href");
			Pattern p = Pattern.compile("/shop/(\\d+?)#(.*)");
			Matcher m = p.matcher(value);
			if (m.find()) {
				int shopId = Integer.parseInt(m.group(1));
				shop.setShopId(shopId);
				if (m.group(2).equals("mr")) {
					shop.setTotalComment(0);
				} else {
					value = tagNode.getText().toString();
					p = Pattern.compile("\\d+");
					m = p.matcher(value);
					if (m.find()) {
						int totalComment = Integer.parseInt(m.group());
						shop.setTotalComment(totalComment);
					}
				}
			}
		}
		
		// average
		results = shopNode.evaluateXPath("/strong[@class='average']");
		
		if (results.length != 0) {
			TagNode tagNode = (TagNode) results[0];
			value = tagNode.getText().toString();
			if (value.equals("-")) {
				shop.setAveragePrice(0);
			} else {
				int average = Integer.parseInt(value.substring(1));
				shop.setAveragePrice(average);
			}
		}
		
		// name
		results = shopNode.evaluateXPath("//li[@class='shopname']/a");
		
		if (results.length != 0) {
			TagNode tagNode = (TagNode) results[0];
			value = tagNode.getText().toString();
			shop.setShortName(value);
		}
		
		// branch
		results = shopNode.evaluateXPath("//li[@class='shopname']/span[@class='tip']/a");
		
		for (Object item : results) {
			value = ((TagNode) item).getAttributeByName("href");
			if (value.startsWith("/search/branch/")) {
				shop.setBranchUrl(WebUtil.getFullUrl(value));
			}
		}
		
		// address, district_id, phone
		results = shopNode.evaluateXPath("//li[@class='address']");
		
		if (results.length != 0) {
			String space = "&nbsp;&nbsp;";
			
			TagNode tagNode = (TagNode) results[0];
			value = tagNode.getText().toString();
			value = value.substring(value.indexOf(':') + 1);
			int index = value.indexOf(space);
			if (index == -1) {
				shop.setAddress(value);
			} else {
				int spaceIndex = value.indexOf(space);
				String address = value.substring(0, spaceIndex);
				String phone = value.substring(spaceIndex + space.length());
				shop.setAddress(address);
				shop.setPhone(phone);
			}
			
			results = tagNode.evaluateXPath("/a");
			if (results.length != 0) {
				tagNode = (TagNode) results[0];
				value = tagNode.getAttributeByName("href");
				Pattern p = Pattern.compile("/search/category/(\\d+).*r(\\d+)");
				Matcher m = p.matcher(value);
				if (m.find()) {
					int districtId = Integer.parseInt(m.group(2));
					shop.setDistrictId(districtId);
				}
			}
		}
		
		// tags
		results = shopNode.evaluateXPath("//li[@class='tags']/a");
		
		if (results.length != 0) {
			StringBuilder sb = new StringBuilder();
			for (Object result : results) {
				TagNode tagNode = (TagNode) result;
				sb.append(tagNode.getText().toString()).append(':');
				value = WebUtil.getFullUrl(((TagNode) result).getAttributeByName("href"));
				sb.append(value).append(',');
			}
			if (sb.length() > 0) {
				sb.deleteCharAt(sb.length() - 1);
			}
			shop.setTags(sb.toString());
		}
		
		return shop;
	}

	private synchronized void markJobProcessedAndsaveShops(String url, List<Shop> shops, Session s) {
		s.beginTransaction();
		markJobProcessed(url, s, shops.size());
		saveShops(shops, s);
		s.getTransaction().commit();
	}
	
	private synchronized int increaseShopsProcessedCount(int count) {
		this.shopsProcessedCount += count;
		return this.shopsProcessedCount;
	}

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		new JobCommand().fetchShops("http://www.dianping.com/search/category/2/10/g328");
	}

	@Override
	protected List<? extends Runnable> getNextBatchWorks() {
		Session s = DpDb.getInstance().openSession();
		String query = String.format("from Job job where job.status = %d", JobStatus.STATUS_NONE);
		@SuppressWarnings("unchecked")
		List<Job> jobs = s.createQuery(query).setMaxResults(getDbReadBatchSize()).list();
		s.close();
		List<Workitem> works = new ArrayList<JobCommand.Workitem>();
		for (Job job : jobs) {
			works.add(new Workitem(job));
		}
		return works;
	}
	
	class Workitem implements Runnable {
		
		private Job job;

		public Workitem(Job job) {
			this.job = job;
		}

		@Override
		public void run() {
			String url = job.getUrl();
			List<Shop> shops = null;
			
			try {
				shops = fetchShops(url);
			} catch (Exception e) {
				e.printStackTrace();
				return;
			}
			
			Session s = DpDb.getInstance().openSession();
			markJobProcessedAndsaveShops(url, shops, s);
			s.close();
			int newCount = shops.size();
			String status = String.format("%d | %s", increaseShopsProcessedCount(newCount), getElapseTimeString());
			status = String.format("\r%-10d%40s", shops.get(newCount - 1).getShopId(), status);
			System.out.print(status);
		}
	}
}
